package com.qiteng.apigateway.filter;

import com.guazai.common.pojo.InterfaceInfo;
import com.guazai.common.pojo.User;
import com.guazai.common.rpc.InterfaceInfoRPC;
import com.guazai.common.rpc.UserInterfaceInfoRPC;
import com.guazai.common.rpc.UserRPC;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;

import static com.qiteng.apiclientsdk.utils.SignUtils.generateSign;

/**
 * @author qiteng
 */
@Component
@Slf4j
public class ApiGlobalFilter implements GlobalFilter, Ordered {

    @DubboReference
    private UserInterfaceInfoRPC userInterfaceInfoRPC;

    @DubboReference
    private InterfaceInfoRPC interfaceInfoRPC;

    @DubboReference
    private UserRPC userRPC;

    private static final List<String> IP_WHITE_LIST = Arrays.asList("127.0.0.1");
    public static final String INTERFACE_HOST = "http://localhost:8123";

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpResponse response = exchange.getResponse();
        // 1. 请求日志
        log.info("全局过滤器开始鉴权...");
        ServerHttpRequest request = exchange.getRequest();
        log.info("请求唯一标识：" + request.getId());
        String path = request.getPath().value();
        log.info("接口url：", INTERFACE_HOST + path);
        String method = request.getMethodValue();
        log.info("请求方法：" + method);
        log.info("请求参数：" + request.getQueryParams());
        String source = request.getRemoteAddress().getHostString();
        log.info("请求来源地址：" + request.getRemoteAddress());
        log.info("本地地址：" + request.getLocalAddress().getHostString());
        // 2. 访问控制 - 黑白名单
        if (!IP_WHITE_LIST.contains(source)) {
            response.setStatusCode(HttpStatus.FORBIDDEN);
        }
        // 3. 用户鉴权（Api签名认证：判断 ak、sk 是否合法）
        HttpHeaders headers = request.getHeaders();
        String accessKey = headers.getFirst("accessKey");
        String clientSign = headers.getFirst("sign");
        String body = headers.getFirst("body");
        String nonce = headers.getFirst("nonce");
        String timestamp = headers.getFirst("timestamp");
        // 根据ak从数据库中查找sk，如果不存在则抛出异常(远程调用)
        User user = userRPC.getUserByAk(accessKey);
        if (user == null) {
            return handleNoAuth(response);
        }
        String secretKey = user.getSecretKey();
        //根据sk和body计算出sign
        String serverSign = generateSign(body, secretKey);
        //比较sign是否相同
        if (!clientSign.equals(serverSign)) {
            return handleNoAuth(response);
        }
        //查看时间是否在5分钟之内
        // 将输入的时间戳转换为 Instant 对象
        long timestampOfLong = Long.parseLong(timestamp);
        Instant inputTime = Instant.ofEpochMilli(timestampOfLong);
        // 获取当前时间的 Instant 对象
        Instant currentTime = Instant.now();
        // 计算时间差
        Duration duration = Duration.between(inputTime, currentTime);
        // 获取时间差的绝对值（以防输入时间在当前时间之后）
        long differenceInMinutes = Math.abs(duration.toMinutes());
        // 检查时间差是否在五分钟之内
        if (differenceInMinutes > 5) {
            return handleNoAuth(response);
        }
        //查看随机数是否在内存中
        log.info("鉴权通过");

        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            HttpStatus statusCode = response.getStatusCode();
            InterfaceInfo interfaceInfo = interfaceInfoRPC.getByPathAndMethod(path, method);
            // 7. 调用成功，接口调用次数 + 1 invokeCount
            if (statusCode.equals(HttpStatus.OK)) {
                log.info("接口调用成功！");
                userInterfaceInfoRPC.invokeCount(interfaceInfo.getId(), user.getId());
            } else {
                // 8. 调用失败
                log.error("接口调用失败");
            }
        }));


    }

    private static Mono<Void> handleNoAuth(ServerHttpResponse response) {
        response.setStatusCode(HttpStatus.FORBIDDEN);
        return response.setComplete();
    }

    @Override
    public int getOrder() {
        return -1;
    }
}
